(ns clojure-demo.lifes3
  :use [clojure.pprint])

;--
;返回细胞的8个邻居的坐标。
(defn neighbours
    [[x y]]
    (for [dx [-1 0 1] dy [-1 0 1] :when (not= 0 dx dy)]
        [(+ dx x) (+ dy y)]))

;--
;计算细胞的活邻居数。
(defn count-neighbours
    [board loc]
    (count (filter #(get-in board %) (neighbours loc)))) 
;(filter #(get-in board %) (neighbours loc)) 返回活着的邻居的坐标。

;--
;繁衍函数，得到下一代细胞图。

(defn indexed-step
    "Yields the next state of the board, using indices to determine neighbors,liveness, etc."
    [board]
    (let [  w (count board)
            h (count (first board))]	;这里假定了板子是矩形。
        (loop   [new-board board x 0 y 0]
                (cond
                    (>= x w) new-board		;处理完毕，返回新板子。
                    (>= y h) (recur new-board (inc x) 0)		;逐行递归
                    :else
                    (let [  new-liveness
                            (case (count-neighbours board [x y])
                                  2 (get-in board [x y])	;当有两个活邻居时，格子维持原样。
                                  3 :on	;当有三个活邻居时，维持活着或复活。
                                  nil)]
                         (recur (assoc-in new-board [x y] new-liveness) x (inc y)))))))		;更新[x,y]格子的状态，逐列递归

;----
;使用两层reduce消除loop。
;先逐列规约，再逐行规约。

(defn indexed-step2
    [board]
    (let [  w (count board)
            h (count (first board))]
        (reduce
            (fn [new-board x]
                (reduce
                    (fn [new-board y]
                        (let [  new-liveness
                                (case (count-neighbours board [x y])
                                      2 (get-in board [x y])
                                      3 :on
                                      nil)]
                            (assoc-in new-board [x y] new-liveness)))
                    new-board (range h)))
            board (range w))))

;----
;两层reduce可以变成一层reduce。
;逐单元格规约。

(defn indexed-step3
    [board]
    (let [  w (count board)
            h (count (first board))]
         (reduce
            (fn [new-board [x y]]
                (let [     new-liveness
                         (case (count-neighbours board [x y])
                               2 (get-in board [x y])
                               3 :on
                               nil)]
                    (assoc-in new-board [x y] new-liveness)))
            board (for [x (range h) y (range w)] [x y]))))

;;;;;;;;;;;;
;生成细胞图（二维向量）。
(defn empty-board
    "Creates a rectangular empty board of the specified width and height."
    [w h]
    (vec (repeat w (vec (repeat h nil)))))

;--
;初始化细胞图。
(defn populate
    "Turns :on each of the cells specified as [y, x] coordinates."
    [board living-cells]   ;living-cells，活格子的坐标
    (reduce (fn [board coordinates]
                (assoc-in board coordinates :on))
            board
            living-cells))

(def glider (populate (empty-board 6 6) #{[2 0] [2 1] [2 2] [1 2] [0 1]}))

(pprint glider)

(-> (iterate indexed-step glider) (nth 8) pprint)
; [[nil nil nil nil nil nil]
; [nil nil nil nil nil nil]
; [nil nil nil :on nil nil]
; [nil nil nil nil :on nil]
; [nil nil :on :on :on nil]
; [nil nil nil nil nil nil]]